/**
 * @author hammady
 */

function Point(x, y) {
	var ana = this
	this.x = parseInt(x)
	this.y = parseInt(y)
	this.stepX = function(step) {
		return new Point(ana.x + step, ana.y)
	}
	this.stepY = function(step) {
		return new Point(ana.x, ana.y + step)
	}
	this.isSafe = function() {
		return ana.x >= 0 && ana.x <= limitX && ana.y >= 0 && ana.y <= limitY &&
			(!gameData[ana.x] || !gameData[ana.x][ana.y])
	}
	this.equals = function(p2) {
		return p2 && this.x == p2.x && this.y == p2.y
	}
	this.distance = function(p2) {
		return new Point(p2.x - this.x, p2.y - this.y)
	}
}

function Move(p1, p2) {
	var ana = this
	this.p1 = p1
	this.p2 = p2
	this.equals = function(move2) {
		return move2 && this.p1.equals(move2.p1) && this.p2.equals(move2.p2)
	}
	this.reverseMove = function() {
		return new Move(this.p2, this.p1)
	}
	this.distance = function() {
		return this.p1.distance(this.p2)
	}
	this.direction = function() {
		return (ana.p1.x < ana.p2.x || ana.p1.y < ana.p2.y) ? 1 : -1
	}
}

function Car(color, orient, length, x, y, element) {
	var ana = this
	this.color = color
	this.orient = orient
	this.length = parseInt(length)
	this.pos = new Point(x, y)
	this.element = $(element)
	this.setElement = function(element) {
		this.element = $(element)
	}
	
	this.image = function () {
		return '/images/' + (ana.color == 'yellow' ? 'x' : ana.length) + ana.orient + '.png'
	}

	this.getFootPrint = function() {
		var fp = []
		var p = ana.pos
		var x = p.x
		var y = p.y
		if (ana.orient == 'h')
			for (var i = 0; i < ana.length; i++)
				fp.push(new Point(x++, p.y))
		else
			for (var i = 0; i < ana.length; i++)
				fp.push(new Point(p.x, y++))
		return fp;
	}
	
	this.saveFootPrint = function() {
		var footprint = ana.getFootPrint()
		for (var i = 0; i < footprint.length; i++) {
			var p = footprint[i]
			if (!gameData[p.x]) gameData[p.x] = {}
			gameData[p.x][p.y] = 1
		}
	}
	
	this.deleteFootPrint = function() {
		var footprint = ana.getFootPrint()
		for (var i = 0; i < footprint.length; i++) {
			var p = footprint[i]
			gameData[p.x][p.y] = null
		}
	}
	
	var neighbourRight = function(step) {
		return ana.pos.stepX(ana.length - 1 + step)
	}
	var neighbourLeft = function(step) {
		return ana.pos.stepX(step)
	}
	var neighbourDown = function(step) {
		return ana.pos.stepY(ana.length - 1 + step)
	}
	var neighbourUp = function(step) {
		return ana.pos.stepY(step)
	}
	
	var getMovesInOrient = function(moves, initialStep) {
		var neighbourFunc
		var stepFunc
		var o = ana.pos
		if (ana.orient == 'h') {
			neighbourFunc = initialStep == 1 ? neighbourRight : neighbourLeft
			stepFunc = o.stepX
		}
		else {
			neighbourFunc = initialStep == 1 ? neighbourDown : neighbourUp
			stepFunc = o.stepY
		}
		var step = initialStep
		while(neighbourFunc(step).isSafe()) {
			moves.push(new Move(o, stepFunc(step)))
			step = step + initialStep * 1
		}
	}
	
	this.getMoves = function() {
		var moves = []
		getMovesInOrient(moves, 1)
		getMovesInOrient(moves, -1)
		return moves.reverse()
	}

	this.getGoalMove = function() {
		return new Move(ana.pos, ana.pos.stepX(limitX - ana.length - ana.pos.x + 1))
	}
		
	this.move = function(move) {
		ana.deleteFootPrint()
		ana.pos = new Point(move.p2.x, move.p2.y)
		ana.saveFootPrint()
		//ana.animateMove(move)
	}

	this.animateMove = function(move) {
		var diff = move.distance()
		new Effect.Move(this.element, {x: diff.x * BLOCK_WIDTH,
			y: diff.y * BLOCK_WIDTH, mode: 'relative', duration: 0.5});
	}
}

var carsRepo = [
	new Car('yellow', 'h', 2),
	new Car('blue', 'h', 2),
	new Car('green', 'h', 3),
	new Car('red', 'v', 2),
	new Car('black', 'v', 3)
]

var initialTop = 0
var BLOCK_WIDTH = 50
var HALF_BLOCK_WIDTH = BLOCK_WIDTH / 2
var GAME_AREA_LEFT
var carIndex = 0
var gameData = {}
var limitX = 5
var limitY = 5
var yellowCar
var solution = []

var cars = []

function initGame() {
	initToolbox()
	initGameArea()
}

function initToolbox() {
	var toolbox = $('toolbox_area')
	for (var i = 0; i < carsRepo.length; i++) {
		createToolboxCar(toolbox, carsRepo[i])
	}
}

function initGameArea() {
	GAME_AREA_LEFT = 150 + 1 + 10;//$('toolbox_area').style.marginLeft
	Droppables.add('game_area', {accept: 'draggable',
	onDrop: function(draggable) {
		var x = Math.floor((parseInt(draggable.style.left) + HALF_BLOCK_WIDTH
			- GAME_AREA_LEFT)/BLOCK_WIDTH)
		var y = Math.floor((parseInt(draggable.style.top) + HALF_BLOCK_WIDTH
			+ parseInt(draggable.getAttribute('initialTop')) + 10)/BLOCK_WIDTH)
		var id_parts = draggable.id.split('_')
		if (x >= 0 && y >= 0) {
			createGameCar($('game_area'), 
				new Car(id_parts[1], id_parts[2], id_parts[3], x, y))
		}
	}})
	$('game_area').style.backgroundImage = 'url(/images/bg.png)'
}

function createToolboxCar(toolbox, car) {
	var div = document.createElement('DIV')
	var id = 'car_' + car.color + '_' + car.orient + '_' + car.length
	div.setAttribute('id', id)
	div.style.width = BLOCK_WIDTH + 'px'
	div.style.height = BLOCK_WIDTH + 'px'
	div.setAttribute('initialTop', initialTop)
	if (car.orient == 'h') {
		div.style.width = BLOCK_WIDTH * car.length + 'px'
		initialTop += BLOCK_WIDTH
	}
	else {
		div.style.height = BLOCK_WIDTH * car.length + 'px'
		initialTop += BLOCK_WIDTH * car.length
	}
	//div.style.backgroundColor = car.color
	div.style.backgroundImage = 'url(' + car.image() + ')'
	div.className = "draggable"

	toolbox.appendChild(div)

	new Draggable(id, {ghosting: true, revert: true, onEnd: function(draggable, event) {
		//alert(Object.toJSON(draggable))
	}})
}

function createGameCar(gamearea, car) {
	if (yellowCar && car.color == 'yellow') {
		alert('Only 1 yellow car is allowed')
		return
	}
	// check if position is empty:
	var footprint = car.getFootPrint()
	if (!isSafeFootPrint(footprint)) return;
	
	// reserve position
	car.saveFootPrint()
	
	if (car.color == 'yellow') yellowCar = car
	
	var div = document.createElement('DIV')
	var id = 'car_' + car.color + '_' + car.orient + '_' + car.length + '_' + carIndex
	carIndex++;
	div.setAttribute('id', id)
	div.style.width = BLOCK_WIDTH + 'px'
	div.style.height = BLOCK_WIDTH + 'px'
	if (car.orient == 'h') {
		div.style.width = BLOCK_WIDTH * car.length + 'px'
	}
	else {
		div.style.height = BLOCK_WIDTH * car.length + 'px'
	}
	//div.style.backgroundColor = car.color
	div.style.backgroundImage = 'url(' + car.image() + ')'
	div.className = "draggable"
	div.style.position = 'absolute'
	div.style.left = (car.pos.x * BLOCK_WIDTH + 10) + 'px'
	div.style.top = (car.pos.y * BLOCK_WIDTH + 10) + 'px'

	gamearea.appendChild(div)
	car.setElement(div)

	new Draggable(id, {ghosting: true, revert: true})
	
	cars.push(car)
}

function isSafeFootPrint(footprint) {
	for (var i = 0; i < footprint.length; i++) {
		var p = footprint[i]
		if (p.x > limitX || p.y > limitY)
			return false
		if (gameData[p.x] && gameData[p.x][p.y])
			return false
	}
	return true
}

function solveClicked() {
	solution.push({car: yellowCar})
	try {
		if (solveGame()) {
			solution[0].move = yellowCar.getGoalMove()
			animateSolution(solution)
		}
		else
			alert("Game has no solution!")
	}
	catch(e) {
		alert("I give up, too much thinking for me! ("+depth+")")
	}
}

var depth = 0
function solveGame(forbiddenStep) {
	depth++
	//if (depth > 100) return
	
	if (gameSolved()) {
		return true
	}
	for (var c = 0; c < cars.length; c++) {
		var car = cars[c]
		var moves = car.getMoves()
		for (var m = 0; m < moves.length; m++) {
			var move = moves[m]
			if (forbiddenStep && forbiddenStep.car == car && forbiddenStep.direction == move.direction())
				continue
			var reverseMove = move.reverseMove()
			car.move(move)
			if (solveGame({car: car, direction: reverseMove.direction()})) {
				solution.push({car: car, move: move})
				return true
			}
			else {
				depth--
				car.move(reverseMove)
			}
		}
	}
}

function gameSolved() {
	var moves = yellowCar.getMoves()
	for (var m = 0; m < moves.length; m++) {
		var move = moves[m]
		//alert(move.p2.x + yellowCar.length - 1)
		if (move.p2.x + yellowCar.length - 1 == limitX)
			return true
	}
}

var animation_i

function doAnimate() {
	//document.title = document.title + animation_i
	if (animation_i >= 0) {
		solution[animation_i].car.animateMove(solution[animation_i].move)
		animation_i--
		setTimeout(doAnimate, 500)
	}
}

function animateSolution(solution) {
	document.title = document.title + 'done'
	animation_i = solution.length - 1
	doAnimate()
}
